---
title: TypeBuilder
---


`TypeBuilder` is used to create or modify output schemas at runtime. It's particularly useful when you have dynamic output structures that can't be determined at compile time - like categories from a database or user-provided schemas.

Here's a simple example of using TypeBuilder to add new enum values before calling a BAML function:

**BAML Code**
```baml {4}
enum Category {
  RED
  BLUE
  @@dynamic  // Makes this enum modifiable at runtime
}

function Categorize(text: string) -> Category {
  prompt #"
    Categorize this text:
    {{ text }}

    {{ ctx.output_format }}
  "#
}
```

**Runtime Usage**
<Tabs>
<Tab title="Python" language="python">
```python
from baml_client.type_builder import TypeBuilder
from baml_client import b

# Create a TypeBuilder instance
tb = TypeBuilder()

# Add new values to the Category enum
tb.Category.add_value('GREEN')
tb.Category.add_value('YELLOW')

# Pass the typebuilder when calling the function
result = b.Categorize("The sun is bright", {"tb": tb})
# result can now be RED, BLUE, GREEN, or YELLOW
```
</Tab>

<Tab title="TypeScript" language="typescript">
```typescript
import { TypeBuilder } from '../baml_client/type_builder'
import { b } from '../baml_client'

// Create a TypeBuilder instance
const tb = new TypeBuilder()

// Add new values to the Category enum
tb.Category.addValue('GREEN')
tb.Category.addValue('YELLOW')

// Pass the typebuilder when calling the function
const result = await b.Categorize("The sun is bright", { tb })
// result can now be RED, BLUE, GREEN, or YELLOW
```
</Tab>

<Tab title="Go" language="go">
```go
package main

import (
    "context"
    "fmt"
    
    b "example.com/myproject/baml_client"
)

func main() {
    ctx := context.Background()
    
    // Create a TypeBuilder instance
    tb, err := b.NewTypeBuilder()
    if err != nil {
        panic(err)
    }
    
    // Get the Category enum and add new values
    category, err := tb.Category()
    if err != nil {
        panic(err)
    }
    
    _, err = category.AddValue("GREEN")
    if err != nil {
        panic(err)
    }
    
    _, err = category.AddValue("YELLOW")
    if err != nil {
        panic(err)
    }
    
    // Pass the typebuilder when calling the function
    result, err := b.Categorize(ctx, "The sun is bright", b.WithTypeBuilder(tb))
    if err != nil {
        panic(err)
    }
    
    // result can now be RED, BLUE, GREEN, or YELLOW
    fmt.Printf("Result: %+v\n", result)
}
```
</Tab>

<Tab title="Ruby" language="ruby">
```ruby
require_relative 'baml_client/client'

# Create a TypeBuilder instance
tb = Baml::TypeBuilder.new

# Add new values to the Category enum
tb.Category.add_value('GREEN')
tb.Category.add_value('YELLOW')

# Pass the typebuilder when calling the function
result = Baml::Client.categorize(text: "The sun is bright", baml_options: { tb: tb })
# result can now be RED, BLUE, GREEN, or YELLOW
```
</Tab>
</Tabs>

## Dynamic Types

There are two ways to use TypeBuilder:
1. Modifying existing BAML types marked with `@@dynamic`
2. Creating entirely new types at runtime

### Modifying Existing Types

To modify an existing BAML type, mark it with `@@dynamic`:

<ParamField path="Classes" type="example">
```baml
class User {
  name string
  age int
  @@dynamic  // Allow adding more properties
}
```

**Runtime Usage**
<Tabs>
<Tab title="Python" language="python">
```python
tb = TypeBuilder()
tb.User.add_property('email', tb.string())
tb.User.add_property('address', tb.string())
```
</Tab>

<Tab title="TypeScript" language="typescript">
```typescript
const tb = new TypeBuilder()
tb.User.addProperty('email', tb.string())
tb.User.addProperty('address', tb.string())
```
</Tab>

<Tab title="Ruby" language="ruby">
```ruby
tb = Baml::TypeBuilder.new
tb.User.add_property('email', tb.string)
tb.User.add_property('address', tb.string)
```
</Tab>
</Tabs>
</ParamField>

<ParamField path="Enums" type="example">
```baml
enum Category {
  VALUE1
  VALUE2
  @@dynamic  // Allow adding more values
}
```

**Runtime Usage**
<Tabs>
<Tab title="Python" language="python">
```python
tb = TypeBuilder()
tb.Category.add_value('VALUE3')
tb.Category.add_value('VALUE4')
```
</Tab>

<Tab title="TypeScript" language="typescript">
```typescript
const tb = new TypeBuilder()
tb.Category.addValue('VALUE3')
tb.Category.addValue('VALUE4')
```
</Tab>

<Tab title="Ruby" language="ruby">
```ruby
tb = Baml::TypeBuilder.new
tb.Category.add_value('VALUE3')
tb.Category.add_value('VALUE4')
```
</Tab>
</Tabs>
</ParamField>

### Creating New Types

You can also create entirely new types at runtime:

<Tabs>
<Tab title="Python" language="python">
```python
tb = TypeBuilder()

# Create a new enum
hobbies = tb.add_enum("Hobbies")
hobbies.add_value("Soccer")
hobbies.add_value("Reading")

# Create a new class
address = tb.add_class("Address")
address.add_property("street", tb.string())
address.add_property("city", tb.string())

# Attach new types to existing BAML type
tb.User.add_property("hobbies", hobbies.type().list())
tb.User.add_property("address", address.type())
```
</Tab>

<Tab title="TypeScript" language="typescript">
```typescript
const tb = new TypeBuilder()

// Create a new enum
const hobbies = tb.addEnum("Hobbies")
hobbies.addValue("Soccer")
hobbies.addValue("Reading")

// Create a new class
const address = tb.addClass("Address")
address.addProperty("street", tb.string())
address.addProperty("city", tb.string())

// Attach new types to existing BAML type
tb.User.addProperty("hobbies", hobbies.type().list())
tb.User.addProperty("address", address.type())
```
</Tab>

<Tab title="Ruby" language="ruby">
```ruby
tb = Baml::TypeBuilder.new

# Create a new enum
hobbies = tb.add_enum("Hobbies")
hobbies.add_value("Soccer")
hobbies.add_value("Reading")

# Create a new class
address = tb.add_class("Address")
address.add_property("street", tb.string)
address.add_property("city", tb.string)

# Attach new types to existing BAML type
tb.User.add_property("hobbies", hobbies.type.list)
tb.User.add_property("address", address.type)
```
</Tab>
</Tabs>

## Type Builders

TypeBuilder provides methods for building different kinds of types:

| Method | Returns | Description | Example |
|--------|---------|-------------|---------|
| `string()` | `FieldType` | Creates a string type | `tb.string()` |
| `int()` | `FieldType` | Creates an integer type | `tb.int()` |
| `float()` | `FieldType` | Creates a float type | `tb.float()` |
| `bool()` | `FieldType` | Creates a boolean type | `tb.bool()` |
| `literal_string(value: string)` | `FieldType` | Creates a literal string type | `tb.literal_string("hello")` |
| `literal_int(value: int)` | `FieldType` | Creates a literal integer type | `tb.literal_int(123)` |
| `literal_bool(value: boolean)` | `FieldType` | Creates a literal boolean type | `tb.literal_bool(true)` |
| `list(type: FieldType)` | `FieldType` | Makes a type into a list | `tb.list(tb.string())` |
| `union(types: FieldType[])` | `FieldType` | Creates a union of types | `tb.union([tb.string(), tb.int()])` |
| `map(key: FieldType, value: FieldType)` | `FieldType` | Creates a map type | `tb.map(tb.string(), tb.int())` |
| `add_class(name: string)` | `ClassBuilder` | Creates a new class | `tb.add_class("User")` |
| `add_enum(name: string)` | `EnumBuilder` | Creates a new enum | `tb.add_enum("Category")` |
| `MyClass` | `FieldType` | Reference an existing BAML class | `tb.MyClass.type()` |


In addition to the methods above, all types marked with `@@dynamic` will also appear in the TypeBuilder.

```baml {4}
class User {
  name string
  age int
  @@dynamic  // Allow adding more properties
}
```

```python {2}
tb = TypeBuilder()
tb.User.add_property("email", tb.string())
```

### FieldType

`FieldType` is a type that represents a field in a type. It can be used to add descriptions, constraints, and other metadata to a field.

| Method | Returns | Description | Example |
|--------|---------|-------------|---------|
| `list()` | `FieldType` | Makes a type into a list | `tb.string().list()` |
| `optional()` | `FieldType` | Makes a type optional | `tb.string().optional()` |

### ClassBuilder

`ClassBuilder` is a type that represents a class in a type. It can be used to add properties to a class.

| Method | Returns | Description | Example |
|--------|---------|-------------|---------|
| `add_property(name: string, type: FieldType)` | `ClassPropertyBuilder` | Adds a property to the class | `my_cls.add_property("email", tb.string())` |
| `description(description: string)` | `ClassBuilder` | Adds a description to the class | `my_cls.description("A user class")` |
| `type()` | `FieldType` | Returns the type of the class | `my_cls.type()` |

### ClassPropertyBuilder

`ClassPropertyBuilder` is a type that represents a property in a class. It can be used to add descriptions, constraints, and other metadata to a property.


| Method | Returns | Description | Example |
|--------|---------|-------------|---------|
| `description(description: string)` | `ClassPropertyBuilder` | Adds a description to the property | `my_prop.description("An email address")` |
| `alias(alias: string)` | `ClassPropertyBuilder` | Adds the alias attribute to the property | `my_prop.alias("email_address")` |

### EnumBuilder

`EnumBuilder` is a type that represents an enum in a type. It can be used to add values to an enum.

| Method | Returns | Description | Example |
|--------|---------|-------------|---------|
| `add_value(value: string)` | `EnumValueBuilder` | Adds a value to the enum | `my_enum.add_value("VALUE1")` |
| `description(description: string)` | `EnumBuilder` | Adds a description to the enum value | `my_enum.description("A value in the enum")` |
| `type()` | `FieldType` | Returns the type of the enum | `my_enum.type()` |

### EnumValueBuilder

`EnumValueBuilder` is a type that represents a value in an enum. It can be used to add descriptions, constraints, and other metadata to a value.

| Method | Returns | Description | Example |
|--------|---------|-------------|---------|
| `description(description: string)` | `EnumValueBuilder` | Adds a description to the enum value | `my_value.description("A value in the enum")` |
| `alias(alias: string)` | `EnumValueBuilder` | Adds the alias attribute to the enum value | `my_value.alias("VALUE1")` |
| `skip()` | `EnumValueBuilder` | Adds the skip attribute to the enum value | `my_value.skip()` |


## Adding Descriptions

You can add descriptions to properties and enum values to help guide the LLM:

<Tabs>
<Tab title="Python" language="python">
```python
tb = TypeBuilder()

# Add description to a property
tb.User.add_property("email", tb.string()) \
   .description("User's primary email address")

# Add description to an enum value
tb.Category.add_value("URGENT") \
   .description("Needs immediate attention")
```
</Tab>

<Tab title="TypeScript" language="typescript">
```typescript
const tb = new TypeBuilder()

// Add description to a property
tb.User.addProperty("email", tb.string())
   .description("User's primary email address")

// Add description to an enum value
tb.Category.addValue("URGENT")
   .description("Needs immediate attention")
```
</Tab>

<Tab title="Ruby" language="ruby">
```ruby
tb = Baml::TypeBuilder.new

# Add description to a property
tb.User.add_property("email", tb.string)
   .description("User's primary email address")

# Add description to an enum value
tb.Category.add_value("URGENT")
   .description("Needs immediate attention")
```
</Tab>
</Tabs>

## Creating/modyfing dynamic types with the `add_baml` method

The `TypeBuilder` has a higher level API for creating dynamic types at runtime.
Here's an example:

<Tabs>
<Tab title="Python" language="python">
```python
tb = TypeBuilder()
tb.add_baml("""
  // Creates a new class Address that does not exist in the BAML source.
  class Address {
    street string
    city string
    state string
  }

  // Modifies the existing @@dynamic User class to add the new address property.
  dynamic class User {
    address Address
  }

  // Modifies the existing @@dynamic Category enum to add a new variant.
  dynmic enum Category {
    PURPLE
  }
""")
```
</Tab>

<Tab title="TypeScript" language="typescript">
```typescript
const tb = new TypeBuilder()
tb.addBaml(`
  // Creates a new class Address that does not exist in the BAML source.
  class Address {
    street string
    city string
    state string
  }

  // Modifies the existing @@dynamic User class to add the new address property.
  dynamic class User {
    address Address
  }

  // Modifies the existing @@dynamic Category enum to add a new variant.
  dynmic enum Category {
    PURPLE
  }
`)
```
</Tab>

<Tab title="Ruby" language="ruby">
```ruby
tb = Baml::TypeBuilder.new
tb.add_baml("
  // Creates a new class Address that does not exist in the BAML source.
  class Address {
    street string
    city string
    state string
  }

  // Modifies the existing @@dynamic User class to add the new address property.
  dynamic class User {
    address Address
  }

  // Modifies the existing @@dynamic Category enum to add a new variant.
  dynmic enum Category {
    PURPLE
  }
")
```
</Tab>
</Tabs>

## Common Patterns

Here are some common patterns when using TypeBuilder:

1. **Dynamic Categories**: When categories come from a database or external source
<CodeBlocks>
```python Python
categories = fetch_categories_from_db()
tb = TypeBuilder()
for category in categories:
    tb.Category.add_value(category)
```
```typescript TypeScript
const categories = await fetchCategoriesFromDb()
const tb = new TypeBuilder()
categories.forEach(category => {
    tb.Category.addValue(category)
})
```
```ruby Ruby
categories = fetch_categories_from_db
tb = Baml::TypeBuilder.new
categories.each do |category|
    tb.Category.add_value(category)
end
```
</CodeBlocks>

2. **Form Fields**: When extracting dynamic form fields
<CodeBlocks>
```python Python
fields = get_form_fields()
tb = TypeBuilder()
form = tb.add_class("Form")
for field in fields:
    form.add_property(field.name, tb.string())
```
```typescript TypeScript
const fields = getFormFields()
const tb = new TypeBuilder()
const form = tb.addClass("Form")
fields.forEach(field => {
    form.addProperty(field.name, tb.string())
})
```
```ruby Ruby
fields = get_form_fields
tb = Baml::TypeBuilder.new
form = tb.add_class("Form")
fields.each do |field|
    form.add_property(field.name, tb.string)
end
```
</CodeBlocks>

3. **Optional Properties**: When some fields might not be present
<CodeBlocks>
```python Python
tb = TypeBuilder()
tb.User.add_property("middle_name", tb.string().optional())
```
```typescript TypeScript
const tb = new TypeBuilder()
tb.User.addProperty("middle_name", tb.string().optional())
```
```ruby Ruby
tb = Baml::TypeBuilder.new
tb.User.add_property("middle_name", tb.string.optional)
```
</CodeBlocks>

<Warning>
All types added through TypeBuilder must be connected to the return type of your BAML function. Standalone types that aren't referenced won't affect the output schema.
</Warning>

## Testing Dynamic Types

See the [advanced dynamic types tests guide](/guide/baml-advanced/dynamic-runtime-types#testing-dynamic-types-in-baml)
for examples of testing functions that use dynamic types. See also the
[reference](/ref/baml/test) for syntax.

## Future Features

We're working on additional features for TypeBuilder:

- JSON Schema support (awaiting use cases)
- OpenAPI schema integration
- Pydantic model support

If you're interested in these features, please join the discussion in our GitHub
issues.
